這篇接續上一篇來介紹,該如何定義你的 validation helper method。
通過 source code 幾個 helper method 的定義,我們可以發現他們都有固定的模式
這邊參考 rails activemodel/lib/active_model/validations/presence.rb
module ActiveModel
module Validations
class PresenceValidator < EachValidator
def validate_each(record, attr_name, value)
record.errors.add(attr_name, :blank, options) if value.blank?
end
end
module HelperMethods
# ... 很多 comments
def validates_presence_of(*attr_names)
validates_with PresenceValidator, _merge_attributes(attr_names)
end
end
end
end
沒錯!除了都在 ActiveModel::Validations
底下建立 validator 之外,
還有在 ActiveModel::Validations::HelperMethods
底下去 define 他的 helper method
但其實延續上一篇講的,const_get
的功能是會往外一層一層找 constant ,所以您的 validator 不一定要跟隨 rails 的腳步定義在 ActiveModel::Validations
裡面,
但您的 validation helper method 如果想做到全專案共用,就一定要設在 ActiveModel::Validations::HelperMethods
裡面!
而且還不僅止於此,因為這個 validation helper method 您勢必要用在你的 class definition 階段,所以一定要早在 rails autoload 之前去定義這些 method。
對沒錯,你需要放在 config/initializers
裡面。
For example,延續上一篇的 csv headers validator
path: app/validators/headers_validator.rb
class HeadersValidator < ActiveModel::EachValidator
def validate_each(record, attr_name, csv)
return if (options[:in] & csv.headers) == options[:in]
record.errors.add(attr_name, :invalid_headers)
end
def check_validity!
unless options[:in].is_a?(Array)
raise "should be array."
end
end
end
然後在 initializers 裡面去定義:
path: config/initializers/validations.rb
module ActiveModel
module Validations
module HelperMethods
def validates_headers_of(*attr_names)
validates_with HeadersValidator, _merge_attributes(attr_names)
end
end
end
end
這樣,你的 Active Model
就會多出一個 validates_headers_of
的神奇功能可以用啦~
依照上述範例,使用方式就像這樣:
class MyClass
include ActiveModel::Model
validates_headers_of :csv, in: ["name", "age"]
def csv
# your_file 考考讀者您要怎麼定義,反正就是傳個檔案進來
@csv ||= CSV.parse(your_file, headers: true)
end
end
至此,validator 系列就算告一段落啦~!